#pragma once


#include "object/Object.hpp"


class Sphere : public Object
{
public:
    Sphere(const Point& center, double radius): _center(center), _radius(radius)
    {
        if (_radius < 0)
            _radius = 1.0;
    }

    virtual BoundBox getBoundBox() const override
    {
        return BoundBox(_center.x - _radius,
                        _center.x + _radius, 
                        _center.y - _radius, 
                        _center.y + _radius, 
                        _center.z - _radius, 
                        _center.z + _radius);
    }

    virtual std::unique_ptr<HitResult> hitTest(const Ray& ray) const override
    {
        Vector rayOriginToCenter = _center - ray.origin();
        double radiusSquare = _radius * _radius;

        // check if ray origin is inside sphere
        if (rayOriginToCenter.length2() < radiusSquare)
            return nullptr;

        // get ray parameter of point on ray closest to sphere center
        double rayParameterNearest = ray.direction().dot(rayOriginToCenter);

        // check if sphere is on back of ray
        if (rayParameterNearest < 0)
            return nullptr;

        // squre of length from sphere center to ray
        double lengthSquareCenterToRay = rayOriginToCenter.length2() - rayParameterNearest * rayParameterNearest;

        // check if hit
        if (lengthSquareCenterToRay > radiusSquare)
            return nullptr;

        // get ray parameter of first hit
        double rayParameterFirstHit = rayParameterNearest - sqrt(radiusSquare - lengthSquareCenterToRay);
        Point pointFirstHit = ray.at(rayParameterFirstHit);

        return std::make_unique<HitResult>(
            rayParameterFirstHit,
            pointFirstHit,
            Vector(pointFirstHit - _center).getNormalized()
            );
    }

private:
    Point _center;          // center of sphere
    double _radius;         // radius of sphere
};
